<!DOCTYPE html>
<html>
<head>
<title>Pendulum Diagram</title>
<style>
  body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
  }
  canvas {
    border: 1px solid #ccc;
    background-color: #fff;
  }
</style>
</head>
<body>
<canvas id="pendulumCanvas" width="600" height="580"></canvas>
<script>
const canvas = document.getElementById('pendulumCanvas');
const ctx = canvas.getContext('2d');

// Style settings
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#000';
ctx.fillStyle = '#000';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

// Parameters
const pivot = { x: 300, y: 100 };
const stringL = 280;
const bobR = 20;
const angleDeg = 20;
const angleRad = angleDeg * Math.PI / 180;

// Calculated positions
const eqPos = { x: pivot.x, y: pivot.y + stringL };
const rightPos = {
    x: pivot.x + stringL * Math.sin(angleRad),
    y: pivot.y + stringL * Math.cos(angleRad)
};
const leftPos = {
    x: pivot.x - stringL * Math.sin(angleRad),
    y: pivot.y + stringL * Math.cos(angleRad)
};

// Helper function for arrows
function drawArrow(ctx, fromx, fromy, tox, toy) {
    const headlen = 8;
    const dx = tox - fromx;
    const dy = toy - fromy;
    const angle = Math.atan2(dy, dx);
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(fromx, fromy);
    ctx.lineTo(tox, toy);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
    ctx.restore();
}

// Helper function for arrowhead only
function drawArrowHead(ctx, fromx, fromy, tox, toy) {
    const headlen = 8;
    const dx = tox - fromx;
    const dy = toy - fromy;
    const angle = Math.atan2(dy, dx);
    ctx.beginPath();
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
}


// Draw support
ctx.beginPath();
ctx.moveTo(pivot.x - 80, pivot.y - 10);
ctx.lineTo(pivot.x + 80, pivot.y - 10);
ctx.stroke();
// Hatching
for (let i = 0; i < 16; i++) {
    const x_start = pivot.x - 80 + i * 10;
    ctx.beginPath();
    ctx.moveTo(x_start, pivot.y - 10);
    ctx.lineTo(x_start - 10, pivot.y - 20);
    ctx.stroke();
}
// Pivot point
ctx.beginPath();
ctx.arc(pivot.x, pivot.y, 2, 0, 2 * Math.PI);
ctx.fill();

// Dashed lines
ctx.save();
ctx.setLineDash([5, 5]);

// Horizontal guide lines
ctx.beginPath();
ctx.moveTo(pivot.x - 220, pivot.y);
ctx.lineTo(pivot.x + 220, pivot.y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(pivot.x - 220, eqPos.y);
ctx.lineTo(pivot.x + 220, eqPos.y);
ctx.stroke();

// Equilibrium position (dashed string and bob)
ctx.beginPath();
ctx.moveTo(pivot.x, pivot.y);
ctx.lineTo(eqPos.x, eqPos.y);
ctx.stroke();

ctx.beginPath();
ctx.arc(eqPos.x, eqPos.y, bobR, 0, 2 * Math.PI);
ctx.stroke();

// Left position (dashed string and bob)
ctx.beginPath();
ctx.moveTo(pivot.x, pivot.y);
ctx.lineTo(leftPos.x, leftPos.y);
ctx.stroke();

ctx.beginPath();
ctx.arc(leftPos.x, leftPos.y, bobR, 0, 2 * Math.PI);
ctx.stroke();

ctx.restore(); // Restore to solid lines

// Right position (solid string and bob)
ctx.beginPath();
ctx.moveTo(pivot.x, pivot.y);
ctx.lineTo(rightPos.x, rightPos.y);
ctx.stroke();

ctx.beginPath();
ctx.arc(rightPos.x, rightPos.y, bobR, 0, 2 * Math.PI);
ctx.fill(); // Solid bob
ctx.beginPath();
ctx.arc(rightPos.x, rightPos.y, bobR, 0, 2 * Math.PI);
ctx.stroke();

// Draw L indicator
const L_line_x = 80;
ctx.beginPath();
ctx.moveTo(L_line_x, pivot.y);
ctx.lineTo(L_line_x, eqPos.y);
ctx.stroke();
drawArrowHead(ctx, L_line_x, pivot.y + 1, L_line_x, pivot.y); // Top head
drawArrowHead(ctx, L_line_x, eqPos.y - 1, L_line_x, eqPos.y); // Bottom head
ctx.textAlign = 'right';
ctx.fillText('L', L_line_x - 10, (pivot.y + eqPos.y) / 2);

// Draw oscillations arrow
const osc_arrow_r = stringL + bobR + 30;
const osc_angle_deg = angleDeg + 8;
const osc_angle_rad = osc_angle_deg * Math.PI / 180;
const startAngle = Math.PI / 2 - osc_angle_rad;
const endAngle = Math.PI / 2 + osc_angle_rad;

ctx.beginPath();
ctx.arc(pivot.x, pivot.y, osc_arrow_r, startAngle, endAngle);
ctx.stroke();

// Arrowheads for oscillation
const startX = pivot.x + osc_arrow_r * Math.cos(startAngle);
const startY = pivot.y + osc_arrow_r * Math.sin(startAngle);
drawArrowHead(ctx, startX, startY - 10, startX, startY);

const endX = pivot.x + osc_arrow_r * Math.cos(endAngle);
const endY = pivot.y + osc_arrow_r * Math.sin(endAngle);
drawArrowHead(ctx, endX, endY - 10, endX, endY);


// Labels
ctx.textAlign = 'left';
// string
ctx.beginPath();
ctx.moveTo(rightPos.x + 80, rightPos.y - 100);
ctx.lineTo((pivot.x + rightPos.x) / 2 + 10, (pivot.y + rightPos.y) / 2);
ctx.stroke();
ctx.fillText('string', rightPos.x + 85, rightPos.y - 100);

// bob
ctx.beginPath();
ctx.moveTo(rightPos.x + 60, rightPos.y + 40);
ctx.lineTo(rightPos.x + bobR * Math.cos(Math.PI / 4), rightPos.y + bobR * Math.sin(Math.PI / 4));
ctx.stroke();
ctx.fillText('bob', rightPos.x + 65, rightPos.y + 40);

// equilibrium position
ctx.textAlign = 'right';
ctx.beginPath();
ctx.moveTo(eqPos.x - 80, eqPos.y - 40);
ctx.lineTo(eqPos.x, eqPos.y);
ctx.stroke();
ctx.fillText('equilibrium', eqPos.x - 85, eqPos.y - 40 - 8);
ctx.fillText('position', eqPos.x - 85, eqPos.y - 40 + 8);

// displacement x
ctx.textAlign = 'center';
// The cross 'x' at equilibrium
ctx.save();
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(eqPos.x - 4, eqPos.y - 4);
ctx.lineTo(eqPos.x + 4, eqPos.y + 4);
ctx.moveTo(eqPos.x - 4, eqPos.y + 4);
ctx.lineTo(eqPos.x + 4, eqPos.y - 4);
ctx.stroke();
ctx.restore();

// The arrow and 'x' label at the displaced bob
drawArrow(ctx, rightPos.x + 15, rightPos.y, rightPos.x - 15, rightPos.y);
ctx.fillText('x', rightPos.x, rightPos.y + 20);

// oscillations label
ctx.fillText('oscillations', pivot.x, eqPos.y + bobR + 50);

// Caption
ctx.font = 'bold 16px Arial';
ctx.fillText('Fig. 4.1', pivot.x, canvas.height - 30);
ctx.font = '16px Arial';
ctx.fillText('(not to scale)', pivot.x, canvas.height - 10);

</script>
</body>
</html>